package interval

import (
	"context"
	"fmt"
	"gitee.com/ymofen/gobase"
	"reflect"
	"sync"
	"sync/atomic"
	"time"
)

type intvIem struct {
	interval int64
	lastTime int64
	busyFlag atomic.Int32
	fn       func()
}

type IntervalLockListEngine struct {
	lk  sync.RWMutex
	lst map[interface{}]*intvIem
}

func (this *IntervalLockListEngine) exec(itm *intvIem, nowUnix int64) {

	if !itm.busyFlag.CompareAndSwap(0, 1) {
		return
	}

	if nowUnix-itm.lastTime < itm.interval {
		itm.busyFlag.Store(0)
		return
	}

	go gobase.GoRun(func() {
		defer func() {
			itm.busyFlag.Store(0)
			itm.lastTime = nowUnix
		}()
		itm.fn()

	})
}

func Any2Id(v interface{}) string {
	valType := reflect.TypeOf(v)
	val := reflect.ValueOf(v)
	switch valType.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return fmt.Sprintf("%d", val.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return fmt.Sprintf("%d", val.Uint())
	case reflect.Float32, reflect.Float64:
		return fmt.Sprintf("%v", val.Float())
	case reflect.String:
		return val.String()
	case reflect.Interface, reflect.Pointer:
		return fmt.Sprintf("%p", v)
	default:
		return fmt.Sprintf("%v", v)
	}

}

func (this *IntervalLockListEngine) OpenInterval(id interface{}, interval time.Duration, fn func()) (err error) {
	this.lk.RLock()
	itm := this.lst[id]
	this.lk.RUnlock()
	if itm != nil {
		itm.fn = fn
		itm.interval = int64(interval / time.Second)
		return
	}

	this.lk.Lock()
	defer this.lk.Unlock()
	itm = this.lst[id]
	if itm == nil {
		itm = &intvIem{interval: int64(interval.Seconds()), lastTime: 0, fn: fn}
		this.lst[id] = itm
	}
	return
}

func (this *IntervalLockListEngine) Open(ctx context.Context) {
	gobase.RunContextAndInterval(ctx, time.Millisecond*500, func() {
		nowUnix := time.Now().Unix()
		this.lk.RLock()
		defer this.lk.RUnlock()
		for _, v := range this.lst {
			this.exec(v, nowUnix)
		}
	})
}

func (this *IntervalLockListEngine) CloseInterval(id interface{}) error {
	this.lk.Lock()
	defer this.lk.Unlock()
	delete(this.lst, id)
	return nil
}

func NewIntervalLockMapEngine() *IntervalLockListEngine {
	def := &IntervalLockListEngine{}
	def.lst = make(map[interface{}]*intvIem)
	go def.Open(context.Background())
	return def
}
